#pragma once

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include "log.hpp"
#include <cstring>

extern Log lg;

const int backlog = 10;

enum
{
    SOCKERR = 2,
    BINDERR,
    LISTENERR,
    ACCEPTERR
};

class Sock
{
public:
    Sock()
    {

    }

    void Socket()
    {
        sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd_ < 0)
        {
            lg(Fatal, "socket create err,%d:%s", errno, strerror(errno));
            exit(SOCKERR);
        }
    }

    void Bind(const uint16_t& port)
    {
        int opt = 1;
        // 防止偶发性的服务器无法进行立即重启(tcp协议的时候再说)
        setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));

        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        local.sin_addr.s_addr = INADDR_ANY;

        socklen_t len = sizeof(local);
        int n = bind(sockfd_, (struct sockaddr *)&local, len);
        if (n < 0)
        {
            lg(Fatal, "bind error,%d:%s", errno, strerror(errno));
            exit(BINDERR);
        }
    }

    void Listen()
    {
        int n = listen(sockfd_, backlog);
        if (n < 0)
        {
            lg(Fatal, "listen error,%d:%s", errno, strerror(errno));
            exit(LISTENERR);
        }
    }

    bool Connect(const std::string& serverip,const uint16_t serverport)
    {
        struct sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_port = htons(serverport);
        inet_pton(AF_INET,serverip.c_str(),&server.sin_addr);

        socklen_t len = sizeof(server);
        int n = connect(sockfd_,(struct sockaddr*)&server,len);
        if(n == -1) return false;

        return true;
    }

    int Accept(std::string* clientip,uint16_t* clientport)
    {
        struct sockaddr_in client;
        memset(&client,0,sizeof(client));
        socklen_t len = sizeof(client);

        int sersockfd = accept(sockfd_,(struct sockaddr*)&client,&len);
        if(sersockfd < 0)
        {
            lg(Warning,"accept error,sersockfd:%d",sersockfd);
            return -1;
        }
        *clientport = ntohs(client.sin_port);
        char buffer[16];
        inet_ntop(AF_INET,&client.sin_addr,buffer,sizeof(buffer));
       * clientip = buffer;
        return sersockfd;
    }

    int GetSockfd()
    {
        return sockfd_;
    }

    void Close()
    {
        close(sockfd_);
    }

    ~Sock()
    {
        close(sockfd_);
    }

private:
    int sockfd_;
};
